﻿using System.Collections.Generic;
using System.Linq;
using HK.Core.GUIEx.Protocol.Attribute;
using HK.Core.Utils;
using HK.Editor.GUIEx.Drag;
using HK.Editor.GUIEx.Protocol;
using HK.Editor.GUIEx.Protocol.Drawer;
using HK.Editor.GUIEx.Protocol.Pagination;
using HK.Editor.Utils;
using UnityEditor;
using UnityEngine;

namespace HK.Editor.GUIEx.Drawer
{
    /// <summary>
    /// GUI绘制器 - 分页面板 - string
    /// </summary>
    public class StrPaginationDrawer : PaginationDrawer<string>, IStrPaginationDrawer
    {
        
#region Create

        /// <summary>
        /// 创建GUI绘制器 - 分页面板
        /// </summary>
        /// <param name="iList">字符串列表</param>
        /// <param name="iPagination">分页设定</param>
        /// <param name="iOnSearched">检索回调</param>
        /// <param name="iDrawFromUnity">是否从Unity调用绘制标志位</param>
        /// <param name="iOptions">选项</param>
        /// <returns>GUI绘制器 - 分页面板</returns>
        public static IStrPaginationDrawer Create(List<string> iList, 
            IPagination iPagination, OnSearchBtnClicked iOnSearched = null, 
            bool iDrawFromUnity = false, int iOptions = (int)GUIOperateOptions.None)
        {
            return new StrPaginationDrawer(iList, iPagination, iOnSearched, iDrawFromUnity, iOptions);
        }

#endregion

        /// <summary>
        /// 保护构造函数
        /// </summary>
        /// <param name="iList">字符串列表</param>
        /// <param name="iPagination">分页设定</param>
        /// <param name="iOnSearched">检索回调</param>
        /// <param name="iDrawFromUnity">是否从Unity调用绘制标志位</param>
        /// <param name="iOptions">选项</param>
        protected StrPaginationDrawer(List<string> iList, IPagination iPagination, 
            OnSearchBtnClicked iOnSearched = null, bool iDrawFromUnity = false, 
            int iOptions = (int)GUIOperateOptions.None) : 
            base(null, iPagination, iOnSearched, iDrawFromUnity, iOptions)
        {
            List = iList;
        }


        /// <summary>
        /// 字符串列表
        /// </summary>
        public List<string> List { get; private set; } = null;

        /// <summary>
        /// 总个数
        /// </summary>
        public override int TotalSize => IsArray ? (List?.Count ?? 0) : 0;

#region Contents
        
        /// <summary>
        /// 取得Element标题
        /// </summary>
        /// <param name="iIndex">Element索引</param>
        /// <param name="iStr">字符串</param>
        /// <returns>Element标题</returns>
        protected override GUIContent GetElementCaption(int iIndex, string iStr)
        {
            GUIContent caption = null;
            var captionTxt = string.IsNullOrEmpty(iStr) ? "Element" : iStr;
            if (!ElementsCaptions.TryGetValue(iIndex, out caption))
            {
                caption = UtilsGUIEx.CreateGuiContent($"({iIndex}){captionTxt}");
                ElementsCaptions.Add(iIndex, caption);
            }
            else
            {
                if (null == caption)
                {
                    caption = UtilsGUIEx.CreateGuiContent($"({iIndex}){captionTxt}");
                }
                ElementsCaptions[iIndex] = caption;
            }
            
            return caption;
        }

#endregion
        
#region Controls

        /// <summary>
        /// 生成成员Element的HashCode
        /// </summary>
        /// <param name="iIndex">成员Element的索引</param>
        /// <param name="iTarget">目标成员</param>
        /// <returns>成员Element的HashCode</returns>
        protected override int GenerateElementHashCode(int iIndex, string iTarget)
        {
            var hashTxtDesc = $"{GetType().Name}_{iIndex}_Element";
            return hashTxtDesc.GetHashCode();
        }

#endregion

#region Events
        
        /// <summary>
        /// 追加Element按钮点击时间
        /// </summary>
        protected override void OnAddElementBtnClicked()
        {
            if (!IsArray || null == SelfProperty) return;
                    
            // 自增一个Element
            SelfProperty.arraySize++;
            if (1 < SelfProperty.arraySize)
            {
                SelfProperty.MoveArrayElement(SelfProperty.arraySize - 1, 0);
            }
                    
            ListSelection.Select(0);
                    
            // 重置列表Element标题
            RefreshElementCaptions();
        }

        /// <summary>
        /// 删除Element按钮点击时间
        /// </summary>
        protected override void OnDelElementBtnClicked()
        {
            if (!IsArray || 0 >= VisibleSize) return;

            var selectIndex = ListSelection.First;
            selectIndex = 0 >= selectIndex ? 0 : selectIndex;
                    
            RemoveElementByIndex(selectIndex);
        }
        
        /// <summary>
        /// 移除Element
        /// </summary>
        /// <param name="iIndex">索引</param>
        protected override void RemoveElementByIndex(int iIndex)
        {
            if(!IsArray || 0 >= VisibleSize) return;
            
            // 检索中，则切换Property在总列表总的索引
            var targetIdx = iIndex;
            if (Searching)
            {
                var selectCapsule = SearchResult[iIndex];
                targetIdx = selectCapsule.Index;
            }
            
            if (targetIdx >= 0 && targetIdx < VisibleSize) 
            {
                
                List.RemoveAt(targetIdx);
                ListSelection.Remove(iIndex);

                if (0 < VisibleSize) 
                {
                    ListSelection.Select(Mathf.Max(0, iIndex - 1));
                    // 若检索中
                    if (Searching)
                    {
                        SearchResult = DoSearchByKey(SearchKey);
                    }
                    PressIndex = ListSelection.First;
                }
            }
			
            // 重置列表Element标题
            RefreshElementCaptions();
        }

#endregion

#region Rect
        
        /// <summary>
        /// 头部高度
        /// </summary>
        protected static readonly float  LineHeight = HeaderHeight;

        /// <summary>
        /// 取得Element高度
        /// </summary>
        /// <param name="iIndex">Element索引</param>
        /// <param name="iElement">Element</param>
        /// <returns>Element高度</returns>
        protected override float GetElementHeight(int iIndex, string iElement) => LineHeight;
        
        /// <summary>
        /// 计算子节点元素的总共高度
        /// </summary>
        /// <param name="iParent">父节点Property</param>
        /// <param name="oValidCount">有效个数</param>
        /// <returns>子节点元素的总共高度</returns>
        protected override float CalcElementsTotalHeight(SerializedProperty iParent, out int oValidCount)
        {
            oValidCount = 0;
            var elementTotalHeight = 0.0f;
            if (!IsArray) return elementTotalHeight;

            if (0 >= VisibleSize)
            {
                elementTotalHeight = EmptyListHeight;
            }
            else
            {
                GetVisibleRange(VisibleSize, out var oStartIndex, out var oEndIndex);
                elementTotalHeight = (LineHeight + 1) * (oEndIndex - oStartIndex) - 1;
            }
            
            return elementTotalHeight;
        }        
        
        /// <summary>
        /// 更新列表拖拽区域信息
        /// </summary>
        /// <param name="iDragPosition">拖拽坐标</param>
        /// <param name="iStartIndex">开始索引</param>
        /// <param name="iEndIndex">结束索引</param>
        protected override void UpdateListDragRects(float iDragPosition, int iStartIndex, int iEndIndex) 
        {
            if(null == SelfProperty) return;
            
            DragRectsList.Resize(iStartIndex, iEndIndex - iStartIndex);
            for (var i = iStartIndex; i < iEndIndex; ++i) 
            {
                var elementRect = ElementRects[i];
                var dragElement = new DragElement
                {
                    Property = null,
                    DragOffset = DragPosition - elementRect.y,
                    Rect = elementRect,
                    DesiredRect = elementRect,
                    Selected = ListSelection.Contains(i),
                    StartIndex = i
                };
                DragRectsList[i - iStartIndex] = dragElement;
            }

            // 根据索引重排序
            DragRectsList.SortByIndex();
        }
        
#endregion

#region String

        /// <summary>
        /// 取得列表Element Property
        /// </summary>
        /// <param name="iIndex">索引</param>
        /// <returns>Element Property</returns>
        protected override string GetElementValue(int iIndex)
        {
            if (!IsArray) return null;

            var childCount = VisibleSize;
            if (0 > iIndex || iIndex >= childCount)
            {
                Error("GetElementValue():Invalid element index!(Index:{0}/{1})", iIndex, childCount);
                return null;
            }

            // 检索中
            if (Searching)
            {
                return SearchResult[iIndex].Target;
            }
                    
            return List[iIndex];
        }
        
        /// <summary>
        /// 查找指定Element的索引
        /// </summary>
        /// <param name="iElement">Element</param>
        /// <returns>Element索引</returns>
        protected override int IndexOf(string iElement)
        {
            if (string.IsNullOrEmpty(iElement) || 0 >= VisibleSize) return -1;
			
            var targetIdx = -1;
            var idx = TotalSize;
            while (--idx > -1) 
            {
                var targetElement = Searching ? SearchResult[idx].Target : List[idx];
                if (!UtilsString.Diff(iElement, targetElement)) 
                {
                    targetIdx = idx;
                    break;
                }
            }

            return targetIdx;
        }

#endregion

#region Selections

        /// <summary>
        /// 选中变更事件
        /// </summary>
        /// <param name="iIndex">Element索引</param>
        /// <param name="iSelected">选中/没选中</param>
        protected override void OnSelectionChanged(int iIndex, bool iSelected)
        {
            string targetElement = null;
                            
            if(0 > iIndex || iIndex >= VisibleSize) return;
            targetElement = Searching ? SearchResult[iIndex].Target : List[iIndex];
			                
            if(string.IsNullOrEmpty(targetElement)) return;
            ElementSelected?.Invoke(iIndex, targetElement, iSelected);
        }

#endregion

#region Sort

        /// <summary>
        /// 排序
        /// </summary>
        /// <param name="iAscending">true:升序; false:倒序;</param>
        public override void Sort(bool iAscending)
        {
            if(!IsArray || 0 >= VisibleSize) return;
                            
            // 排序
            List = iAscending ? List.OrderBy(iO => iO).ToList() : List.OrderByDescending(iO => iO).ToList();
                    
            // 应用
            // 刷新标题
            RefreshElementCaptions();
                    
            HandleUtility.Repaint();
        }

#endregion

#region SearchBar

        /// <summary>
        /// 执行检索
        /// </summary>
        /// <param name="iSearchKey">检索Key</param>
        protected override List<SearchCapsule<string>> DoSearchByKey(string iSearchKey)
        {
            if (!IsArray || 0 >= TotalSize) return null;

            if (string.IsNullOrEmpty(iSearchKey))
            {
                Searching = false;
                return null;
            }
                    
            var searchResult = new List<SearchCapsule<string>>();
            var count = TotalSize;
            for (var idx = 0; idx < count; idx++)
            {
                var elementValue = List[idx];
                if(string.IsNullOrEmpty(elementValue)) continue;

                if (!elementValue.ToLower().Contains(iSearchKey.ToLower())) continue;
                var searchCapsule = new SearchCapsule<string> { Index = idx, Target = elementValue };
                searchResult.Add(searchCapsule);
            }
                    
            return searchResult;
        }

#endregion

#region Draw

        /// <summary>
        /// 绘制Element列表
        /// </summary>
        /// <param name="iRect">绘制矩形区域</param>
        /// <param name="iEvent">Event</param>
        /// <returns>绘制范围</returns>
        protected override Rect DrawElements(Rect iRect, Event iEvent)
        {
            var displayRect = iRect;
            
            if (!Dragging) {

                UpdateElementRects(displayRect, iEvent);
            }
            if (0 >= ElementRects.Length) return displayRect;
            if (!GetVisibleRange(out var oStartIndex, out var oEndIndex)) return displayRect;
                            
            var selectableRect = displayRect;
            selectableRect.yMin = ElementRects[oStartIndex].yMin;
            selectableRect.yMax = ElementRects[oEndIndex - 1].yMax;
                            
            HandlePreSelection(selectableRect, iEvent);
            var lastEnable = GUI.enabled;
            GUI.enabled = !ReadOnly;
            
            if (Searching)
            {
                for (var idx = oStartIndex; idx < oEndIndex; ++idx)
                {
                    var elementCapsule = SearchResult[idx];
                    var elementCaption = GetElementCaption(idx, elementCapsule.Target);
                    if(null == elementCaption) continue;

                    var childRect = ElementRects[idx];
                    var selected = ListSelection.Contains(idx);
                
                    childRect = DrawElement(childRect, iEvent, elementCapsule.Index, elementCaption, elementCapsule.Target, selected, selected);
                    childRect.y = childRect.yMax;
                }
            }
            else
            {
                for (var idx = oStartIndex; idx < oEndIndex; ++idx)
                {
                    var elementValue = List[idx];
                    if(string.IsNullOrEmpty(elementValue)) continue;

                    var elementCaption = GetElementCaption(idx, elementValue);
                    if(null == elementCaption) continue;

                    var childRect = ElementRects[idx];
                    var selected = ListSelection.Contains(idx);
                
                    childRect = DrawElement(childRect, iEvent, idx, elementCaption, elementValue, selected, selected);
                    childRect.y = childRect.yMax;
                }
            }
                            
            GUI.enabled = lastEnable;
            HandlePostSelection(selectableRect, iEvent);
                            
            return displayRect;
        }
        
        /// <summary>
        /// 绘制Element
        /// </summary>
        /// <param name="iRect">绘制矩形区域</param>
        /// <param name="iEvent">Event</param>
        /// <param name="iElementIndex">Element索引</param>
        /// <param name="iElementCaption">Element标题</param>
        /// <param name="iElementValue">Element值</param>
        /// <param name="iSelected">选中标志位</param>
        /// <param name="iFocused">焦点</param>
        /// <returns>绘制范围</returns>
        protected virtual Rect DrawElement(Rect iRect, Event iEvent, int iElementIndex, 
            GUIContent iElementCaption, string iElementValue, bool iSelected, bool iFocused)
        {
            var displayRect = iRect;
            
            // 可选中背景
            var bgRect = displayRect;
            var selectBgRect = displayRect;
            selectBgRect.yMax += 1;
            selectBgRect = UtilsGUIEx.DrawListElementBg(selectBgRect, iEvent, iSelected, iFocused);
            
            var dragMarkRect = displayRect;
            dragMarkRect.y = displayRect.yMin + VerticalSpacing;
            dragMarkRect.width = DragMarkWidth;
            dragMarkRect = UtilsGUIEx.DrawDragMark(dragMarkRect, iEvent);

            var captionRect = bgRect;
            captionRect.xMin = dragMarkRect.xMax + HorizontalSpacing;
            captionRect.xMax -= 1;
            captionRect.yMin += 1;
            captionRect.yMax -= 1;
            EditorGUI.TextField(captionRect, iElementValue);
            
            return displayRect;
        }
        
#endregion

    }
}
